package gengine.clientside;

import gengine.clientside.forms.MainForm;
import gengine.common.misc.BaseApplication;
import gengine.common.misc.ClientInfo;
import gengine.common.misc.InconsistentInfoException;
import gengine.common.packets.BasePacket;
import gengine.common.packets.PacketProcessor;
import gengine.common.packets.PacketTypeEnum;
import gengine.common.packets.PingPacket;
import gengine.common.packets.client.ClientInfoPacket;

import javax.swing.*;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;

/**
 * User: arseniy
 * Date: 25.12.11
 */
public class ClientApplication extends BaseApplication {
    public ClientThread clientThread;
    Dictionary<PacketTypeEnum, List<PacketProcessor>> processors = new Hashtable<PacketTypeEnum, List<PacketProcessor>>();
    public ClientInfo clientInfo;
    public List<ClientInfo> ciPlayers = new ArrayList<>();


    public void Subscribe(PacketProcessor processor, PacketTypeEnum type) {
        if (processors.get(type) == null) {
            processors.put(type, new ArrayList<PacketProcessor>());
        }
        List<PacketProcessor> list = processors.get(type);
        list.add(processor);
    }

    public ClientApplication() {
        super();
        this.packetFactory = new ClientPacketFactory();
    }

    public static void createForm() {
        JFrame frame = new JFrame("Client");
        frame.setContentPane(new MainForm());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    @Override
    public void run() {
        super.run();
        javax.swing.SwingUtilities.invokeLater(() -> createForm());
    }

    @Override
    public void quit() {
        clientThread.interrupt();
        super.quit();
    }

    public String connect(String address) {
        disconnect();
        String[] ips = address.split(":");
        if (ips.length < 2)
            return "Invalid ip";
        try {
            Socket socket = new Socket(ips[0], Integer.parseInt(ips[1]));
            clientThread = new ClientThread(socket, this);
            clientThread.start();
            return "Connection established. Your id " + clientThread.getClientId();
        } catch (IOException e) {
            e.printStackTrace();
            return "Can't connect to selected server";
        }
    }

    public Boolean disconnect() {
        if (isConnected())
            clientThread.interrupt();
        else
            return false;
        clientThread = null;
        return true;
    }

    public boolean isConnected() {
        return clientThread != null && clientThread.isAlive();
    }

    public void processPacket(BasePacket packet) {
        PacketTypeEnum type = packet.getPacketType();
        if (processors.get(type) == null) {
            processors.put(type, new ArrayList<PacketProcessor>());
        }
        if (type==PacketTypeEnum.Ping) {
            PingPacket pp = (PingPacket)packet;
            //pong
            if (pp.answer==0) {
                pp.answer=1;
                clientThread.sendPacketImmediately(pp);
                return;
            }
        }

        List<PacketProcessor> list = processors.get(type);
        for (PacketProcessor pp : list) {
            pp.Process(packet);
        }

        //Replace info
        if (type==PacketTypeEnum.ClientInfoPacket) {
            ClientInfoPacket cip = (ClientInfoPacket)packet;
            ClientInfo ci = cip.getClientInfo();
            ClientInfo cur =  getClientInfoById(packet.getClientId());
            if (ci.getId()==clientThread.getClientId()) {
                clientInfo=ci;
            }
            if (cur==null) {
                addClientInfo(packet.getClientId(), ci);
            } else {
                try {
                    ci.Apply(cur);
                } catch (InconsistentInfoException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public ClientInfo getClientInfoById(int id) {
        for (ClientInfo ci : ciPlayers) {
            if (ci.getId() == id)
                return ci;
        }
        return null;
    }

    public void addClientInfo(int id,ClientInfo info) {
        ciPlayers.add(info);
    }


    public void sendClientInfo() {
        clientThread.sendPacketImmediately(new ClientInfoPacket(clientThread.getClientId(),clientInfo));
    }
}
